/**
 * Copyright 2007 Igor Chaves Cananéa
 *
 * This file is part of PBSFW
 *
 * PBSFW is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License.
 *
 * PBSFW is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef NO_CMM

#ifndef _MEMORY_LIST_H_
#define _MEMORY_LIST_H_
#include <stddef.h>
#include <cstdlib>
using namespace std;

#define USE_CMM(CLASS) private:\
static util::MemoryList ml;\
public:\
	void* operator new (size_t size) {\
		return CLASS::ml.alloc(size);\
	}\
\
	void operator delete (void *p) {\
		CLASS::ml.free((util::byte*) p);\
	}\
private:

#define DECLARE_CMM(CLASS) util::MemoryList CLASS::ml;

namespace util {
	typedef unsigned char byte;

	/**
	 * This class implements a list based memory manager that is very efficient
	 * for classes that are allocated and freed frequently. Typically, Events
	 * and Messages are classes that could benefit from the manager.
	 *
	 * If you would like to use this memory manager in your classe, use the
	 * macros USE_CMM(ClassType) and DECLARE_CMM(ClassType) to ease the process.
	 * The USE_CMM(ClassType) should be included in the class definition and the
	 * DECLARE_CMM should be included in the implementation file. For example,
	 * supose that Class A wants to use this memory manager. This is how the
	 * code should look like:
	 \verbatim
	 A.h:
		class A {
		    ...
		    USE_CMM(A)
		    ...
		}

	A.cpp:
		...
		DECLARE_CMM(A)
		...
	 \endverbatim
	 *
	 * Keep in mind that even if you use these macros, you MUST NOT define the
	 * NO_CMM macro to actually enable the custom memory manager. This is
	 * achieved with scons by calling it with the mm parameter set to yes:
	 *
	 *<b> scons mm=yes </b>
	 *
	 * The normal invocation of scons WILL DEFINE NO_CMM, nullifying the use of
	 * the memory manager.
	 */
	class MemoryList {
		private:
			byte *data;
			MemoryList *next;
			MemoryList *prev;
			MemoryList *last;

		public:
			MemoryList() : data(0), next(0), prev(0) {
				this->last = this;
			}

			/**
			 * Returns a previously alloacted memory block, if there is one
			 * available, or allocates a new one with malloc otherwise.
			 *
			 * @param size the size of the requested memory block.
			 * @return an allocated memory block.
			 */
			byte *alloc(size_t size) {
				if (this->last->data == 0) {
					return (byte*) malloc(size);
				}

				byte *data = this->last->data;
				this->last->data = 0;
				if (this->last->prev != 0) {
					this->last = this->last->prev;
				}

				return data;
			}

			/**
			 * Adds the released memory block to the free memory list. If there
			 * is no more available free list blocks, one is created and the
			 * memory block is added to it.
			 *
			 * @param data the memory block that is considered free.
			 */
			void free(byte *data) {
				if (this->last->data == 0) {
					this->last->data = data;

					if (this->last->next != 0) {
						this->last = this->last->next;
					}
				}
				else if (this->last->next != 0) {
					this->last = this->last->next;
					this->last->data = data;
				}
				else {
					MemoryList *l = new MemoryList();

					l->data = data;

					this->last->next = l;
					l->prev = this->last;
					this->last = l;
				}
			}
	};
};

#endif // #ifndef _MEMORY_LIST_H_

#else
	#define USE_CMM(CLASS)
	#define DECLARE_CMM(CLASS)
#endif // #ifndef NO_CMM
